//
// SessionPool.h
//
// Library: Data
// Package: SessionPooling
// Module:  SessionPool
//
// Definition of the SessionPool class.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#ifndef Data_SessionPool_INCLUDED
#define Data_SessionPool_INCLUDED


#include <list>
#include "Poco/Any.h"
#include "Poco/Data/Data.h"
#include "Poco/Data/PooledSessionHolder.h"
#include "Poco/Data/PooledSessionImpl.h"
#include "Poco/Data/Session.h"
#include "Poco/HashMap.h"
#include "Poco/Mutex.h"
#include "Poco/Timer.h"


namespace Poco
{
namespace Data
{


    class Data_API SessionPool : public RefCountedObject
    /// This class implements session pooling for POCO Data.
    ///
    /// Creating a connection to a database is often a time consuming
    /// operation. Therefore it makes sense to reuse a session object
    /// once it is no longer needed.
    ///
    /// A SessionPool manages a collection of SessionImpl objects
    /// (decorated with a PooledSessionImpl).
    ///
    /// When a SessionImpl object is requested, the SessionPool first
    /// looks in its set of already initialized SessionImpl for an
    /// available object. If one is found, it is returned to the
    /// client and marked as "in-use". If no SessionImpl is available,
    /// the SessionPool attempts to create a new one for the client.
    /// To avoid excessive creation of SessionImpl objects, a limit
    /// can be set on the maximum number of objects.
    /// Sessions found not to be connected to the database are purged
    /// from the pool whenever one of the following events occurs:
    ///
    ///   - JanitorTimer event
    ///   - get() request
    ///   - putBack() request
    ///
    /// Not connected idle sessions can not exist.
    ///
    /// Usage example:
    ///
    ///     SessionPool pool("ODBC", "...");
    ///     ...
    ///     Session sess(pool.get());
    ///     ...
    {
    public:
        SessionPool(
            const std::string & connector,
            const std::string & connectionString,
            int minSessions = 1,
            int maxSessions = 32,
            int idleTime = 60);
        /// Creates the SessionPool for sessions with the given connector
        /// and connectionString.
        ///
        /// The pool allows for at most maxSessions sessions to be created.
        /// If a session has been idle for more than idleTime seconds, and more than
        /// minSessions sessions are in the pool, the session is automatically destroyed.

        ~SessionPool();
        /// Destroys the SessionPool.

        Session get();
        /// Returns a Session.
        ///
        /// If there are unused sessions available, one of the
        /// unused sessions is recycled. Otherwise, a new session
        /// is created.
        ///
        /// If the maximum number of sessions for this pool has
        /// already been created, a SessionPoolExhaustedException
        /// is thrown.

        template <typename T>
        Session get(const std::string & name, const T & value)
        /// Returns a Session with requested property set.
        /// The property can be different from the default pool
        /// value, in which case it is reset back to the pool
        /// value when the session is reclaimed by the pool.
        {
            Session s = get();
            _addPropertyMap.insert(AddPropertyMap::value_type(s.impl(), std::make_pair(name, s.getProperty(name))));
            s.setProperty(name, value);

            return s;
        }

        Session get(const std::string & name, bool value);
        /// Returns a Session with requested feature set.
        /// The feature can be different from the default pool
        /// value, in which case it is reset back to the pool
        /// value when the session is reclaimed by the pool.

        int capacity() const;
        /// Returns the maximum number of sessions the SessionPool will manage.

        int used() const;
        /// Returns the number of sessions currently in use.

        int idle() const;
        /// Returns the number of idle sessions.

        int dead();
        /// Returns the number of not connected active sessions.

        int allocated() const;
        /// Returns the number of allocated sessions.

        int available() const;
        /// Returns the number of available (idle + remaining capacity) sessions.

        std::string name() const;
        /// Returns the name for this pool.

        static std::string name(const std::string & connector, const std::string & connectionString);
        /// Returns the name formatted from supplied arguments as "connector:///connectionString".

        void setFeature(const std::string & name, bool state);
        /// Sets feature for all the sessions.

        bool getFeature(const std::string & name);
        /// Returns the requested feature.

        void setProperty(const std::string & name, const Poco::Any & value);
        /// Sets property for all sessions.

        Poco::Any getProperty(const std::string & name);
        /// Returns the requested property.

        void shutdown();
        /// Shuts down the session pool.

        bool isActive() const;
        /// Returns true if session pool is active (not shut down).

    protected:
        virtual void customizeSession(Session & session);
        /// Can be overridden by subclass to perform custom initialization
        /// of a newly created database session.
        ///
        /// The default implementation does nothing.

        typedef Poco::AutoPtr<PooledSessionHolder> PooledSessionHolderPtr;
        typedef Poco::AutoPtr<PooledSessionImpl> PooledSessionImplPtr;
        typedef std::list<PooledSessionHolderPtr> SessionList;
        typedef Poco::HashMap<std::string, bool> FeatureMap;
        typedef Poco::HashMap<std::string, Poco::Any> PropertyMap;

        void purgeDeadSessions();
        int deadImpl(SessionList & rSessions);
        void applySettings(SessionImpl * pImpl);
        void putBack(PooledSessionHolderPtr pHolder);
        void onJanitorTimer(Poco::Timer &);

    private:
        typedef std::pair<std::string, Poco::Any> PropertyPair;
        typedef std::pair<std::string, bool> FeaturePair;
        typedef std::map<SessionImpl *, PropertyPair> AddPropertyMap;
        typedef std::map<SessionImpl *, FeaturePair> AddFeatureMap;

        SessionPool(const SessionPool &);
        SessionPool & operator=(const SessionPool &);

        void closeAll(SessionList & sessionList);

        std::string _connector;
        std::string _connectionString;
        int _minSessions;
        int _maxSessions;
        int _idleTime;
        int _nSessions;
        SessionList _idleSessions;
        SessionList _activeSessions;
        Poco::Timer _janitorTimer;
        FeatureMap _featureMap;
        PropertyMap _propertyMap;
        bool _shutdown;
        AddPropertyMap _addPropertyMap;
        AddFeatureMap _addFeatureMap;
        mutable Poco::Mutex _mutex;

        friend class PooledSessionImpl;
    };


    inline std::string SessionPool::name(const std::string & connector, const std::string & connectionString)
    {
        return Session::uri(connector, connectionString);
    }


    inline std::string SessionPool::name() const
    {
        return name(_connector, _connectionString);
    }


    inline bool SessionPool::isActive() const
    {
        return !_shutdown;
    }


}
} // namespace Poco::Data


#endif // Data_SessionPool_INCLUDED
